<?php
/**
 *  ################################################################################################
 *  ################################################################################################
 * 
 *		还需检查并解决的问题：
 *			1、.+?匹配的问题，如果html中不规范，例如<a> 中间没有任何字符，会导致匹配错误。核实所有的.+?匹配
 * 
 *  ################################################################################################
 *  ################################################################################################
 * 
 */
class content_clip {
	private $content;
	private $result;
	private $img_ignor;
	private $img_ok;
	private $base_url;
	
	private $before_body;
	private $after_body;
	private $style;

	public $img_width_max;		// 如果图片的宽度大于等于这个数值，则被标记为大图片
	public $img_height_max;		// 如果图片的高度大于等于这个数值，则被标记为大图片
	public $img_local_path;
	public $img_www_path;
	
	public function display () {
		$return = '';
		if (is_array($this->result['per_len'])) {
			$return .= '<table border=1 cellpadding=5 width=600>';
			$return .= '<tr>
						<th>K</th>
						<th>Per</th>
						<th>Len(txt/img)</th>
						<th>Per A</th>
						<th>txt in html</th>
						<th>have big</th>
					</tr>';
			foreach ($this->result['per_len'] as $k=>$v) {
				$return .= '
					<tr>
						<td style="background-color:#000; padding:0 3px; color:#FFF;">'.$k.'</td>
							<td style="background-color:#FFF; padding:0 3px;">'.$v.'</td>
						<td style="background-color:#FFD; padding:0 3px;">'.$this->result['len'][$k].'('.$this->result['txt_len'][$k].' / '.$this->result['img_len'][$k].')</td>
						<td style="background-color:#FFD; padding:0 3px;">'.$this->result['per_a'][$k].'</td>
						<td style="background-color:#FDF; padding:0 3px;">'.$this->result['txt_in_html'][$k].'</td>
						<td style="background-color:#FDD; padding:0 3px;">'.(($this->result['big_img'][$k]) ? 'BIGIMG' : 'No').'</td>
					</tr>
					';
				if ($this->result['txt'][$k]) $return .= '<tr><td colspan=7 style="word-break:break-all;">'.$this->result['txt'][$k].'</td></tr>';
				$return .= '<tr><td colspan=7 style="word-break:break-all;"><textarea style="width:600px; height:14px; padding:0;">'.$this->result['html'][$k].'</textarea></td></tr>';
			}
			$return .= '</table>';
		}
		return $return;
	}
	public function __construct() {
		$this->img_local_path = UPYUN_IMG_LOCAL_PATH;
		$this->img_www_path = UPYUN_IMG_WWW_PATH;
		$this->img_width_max = 350;
		$this->img_height_max = 121;
	}
	private function parse_pre () {
		// 在第一步中已经去掉了script、注释、iframe、form标签、input标签
		// 删除掉无用的部分
		$this->content = preg_replace( '/\n|\r|\t/',	'',	$this->content );		// 去掉换行、回车、制表符
		$this->content = preg_replace( '/\s+/',		' ',	$this->content );		// 去掉连续空格，注意：多个空格替换成1个空格
		
		// 把有用的部分保留下来
		preg_match('/^.*?<body.*?>/i', $this->content, $match);				// 记录下body前面的部分，最后返回结果的时候在添加回去
		$this->before_body = $match[0];
		preg_match('/<\/body.*?>.*?$/i', $this->content, $match);				// 记录下body后面的部分，最后返回结果的时候在添加回去
		$this->after_body = $match[0];
		preg_match_all('/<style.*?>.*?<\/style>|<link\s+.*?>/i', $this->content, $match);		// 记录下所有style和link，最后返回结果的时候在添加回去
		if (is_array($match[0])) {
			foreach ($match[0] as $v) {
				$this->style .= $v;
			}
		}
		
		// 继续删除
		$this->content = preg_replace('/^.*?<body.*?>/i',		'',	$this->content);		// 去掉body前面的部分
		$this->content = preg_replace('/<\/body.*?>.*?$/i',		'',	$this->content);		// 去掉body后面的部分
		/**
		 * 遇到的特殊情况：http://travel.163.com/13/0306/17/8PA3JF0200063KE8_all.html
		 *	页面中包括hidden textarea，其中的内容是html代码，所以在解析block之前，把textarea和input都去掉，避免影响block的分析结果，也避免造成乱码
		 */
		$this->content = preg_replace('/<style.*?>.*?<\/style>|<link\s+.*?>|<textarea.*?>.*?<\/textarea>|&nbsp;|&.{1,5};|&#.{1,5};/i',	'',	$this->content );		// 去掉style，特殊字符
	}
	private function parse_html_to_block () {
		preg_match('/^(.+?)(<div.*?>|<\/div>|<table.*?>|<\/table>)/i', $this->content, $match);
		if ($match) {
			if (strtolower($match[2]) == '</div>' or strtolower($match[2]) == '</table>') {
				/* 
				 * [0] =	<div class="123">def</div>		// 这个是我们要的结果
				 * [1] =	<div class="123">def
				 * [2]=	</div>
				 */
				$match = $match[0];
			} else {
				/*
				 * [0] = txt<div class="123">
				 * [1] = txt							// 这个是我们要的结果
				 * [2] = <div class="123">
				 */ 
				$match = $match[1];
			}
			// 处理match中的正文部分
			$this->parse_block_to_info($match);		// 成功后记录结果
			
			// 截掉content中前面的部分，剩下的部分继续parse
			$this->content = substr($this->content, strlen($match));
			if ($this->content) {
				$this->parse_html_to_block();
			}
		} elseif ($this->content) {
			// 剩下的已经没有div标签了
			$this->parse_block_to_info($this->content);	// 成功后记录结果
			unset($this->content);
		}
	}
	private function parse_block_to_info ($html) {
		if ($html) {
			$parse_img	= $this->parse_img_in_block($html);				// (big_img_urls, img_as_byte)
			$txt		= trim(preg_replace('/\s+/', ' ', $this->function_strip_tags($html)));	// 去掉html标签，然后去掉文本中的连续空格和首尾空格
			
			$this->result['html'][]		= $html;
			$this->result['big_img'][]	= $parse_img['big_img_urls'];
			$this->result['per_a'][]		= $this->per_a($html);				// 计算a链接的比例
			$this->result['txt'][]		= $txt;
			$this->result['txt_len'][]		= strlen($txt);					// 文本的长度
			$this->result['img_len'][]	= $parse_img['img_as_byte'];			// 把大图片作为txt len附加到block信息中
			$this->result['len'][]		= strlen($txt)+ $parse_img['img_as_byte'];
			$this->result['txt_in_html'][]	= $this->per_txt_in_html($html);
			// $this->result['per_len']     在analyze pre中会计算
			
			return true;
		} else return false;
	}
	private function analyse_pre () {
		if (is_array($this->result['len'])) {
			// 计算len的总和
			$len_total = 0;
			foreach ($this->result['len'] as $v) {
				$len_total += $v;
			}
			// 计算每个block的len的百分比
			if ($len_total>0) {
				foreach ($this->result['len'] as $v) {
					$this->result['per_len'][] = intval(floor($v / $len_total * 100));			// 计算长度的百分比
				}
			}
			return true;
		} else return false;
	}
	/*
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 *	第三步：
	 *		1、parse_pre：
	 *			去掉注释、iframe、script、连续空格、制表符、回车换行、style、textarea、input。
	 *			匹配出body之前和之后的部分和所有style和link以备完成之后再带回去
	 *		2、parse_html_to_block：
	 *			以<div>|</div>|<table>|</table>为分隔符将html分解成block，然后parse_block_to_info将结果记录在result中，
	 *			对于每一个block，计算它的big_img(图片地址的数组)、per_a(a链接占文本的百分比)、txt(文本)、txt_len(文本长度)、
	 *			img_len(把大图片模拟成文字长度)、len（文本和图片的总长度）、txt_in_html（文本占html的比例）、html(原始html)
	 *		3、analyse_pre：
	 *			遍历block，计算出每一个block长度的百分比
	 *		4、analyse_center_blocks：
	 *			寻找中心的block组，规则：排序找出最大的block(rule_0)，然后最大的block的百分比向下浮动30%来查找近似的block组成中心block组，
	 *			如果没有找到center block，那么尝试找出三张大图
	 *		5、analyse：
	 *			按顺序记录center blocks的内容，以center blocks为中心，向上、向下分别查找big block(is_big_block(rule_1、2、3、4))，
	 *			查找的时候，如果big block之间的小block的数量超过一定数量（6），则中断查找（意思是距离center太远的部分，就认为其不是正文）
	 *			查找的时候，顺便记录大图和摘要
	 *			如果没能找到center blocks，那么尝试用2~3张大图来作为summary
	 *			注：返回的时候检查before body部分的内容中如果包括<html，则返回clip，否则不返回clip（即：选中保存的不裁剪，全部页面才裁剪）
	 *			如果以上都没有，那么clip不成功
	 * 
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 */
	public function analyse ($content, $base_url) {
		$this->content = $content;
		$this->base_url = $base_url;
		
		$this->parse_pre();
		$this->parse_html_to_block();
		$this->analyse_pre();

		$tmp		= '';
		$clip		= '';
		$big_img	= '';
		$summary	= '';
		$small_block_number = 0;
		
		$return = false;
		if (is_array($this->result['len'])) {
			// 先查找最大的中心block
			$k_center = $this->analyse_center_blocks();
			if (isset($k_center['min']) and isset($k_center['max'])) {
				// 记录center部分
				for ($i=$k_center['min']; $i<=$k_center['max']; $i++) {
					$clip		.= $this->result['html'][$i];
					// 记录大图
					if (empty($big_img) and $this->result['big_img'][$i]) {
						$big_img = $this->result['big_img'][$i][0];
					}
					// 记录摘要
					if (strlen($summary) < 100 and $this->result['txt_len'][$i]>strlen($summary)) {
						$summary = $this->result['txt'][$i];
					}
				}
				// 向前查找
				for ($i = $k_center['min'] -1 ; $i >= 0 ; $i--) {
					if ($small_block_number < 6) {
						// 记录大图
						if (empty($big_img) and $this->result['big_img'][$i]) {
							$big_img = $this->result['big_img'][$i][0];
						}
						if ($this->analyse_is_big_block($i)) {
							$clip = $tmp . $clip;
							$clip = $this->result['html'][$i] . $clip;
							$tmp = '';
							$small_block_number = 0;
							// 记录摘要
							if (strlen($summary) < 100 and $this->result['txt_len'][$i]>strlen($summary)) {
								$summary = $this->result['txt'][$i];
							}
						} else {
							$tmp = $this->result['html'][$i] . $tmp;
							$small_block_number ++;
						}
					} else {
						$tmp = '';
						$small_block_number = 0;
						break;
					}
				}

				$small_block_number = 0;
				$tmp = '';
				// 向后查找
				for ($i = $k_center['max'] +1 ; $i < sizeof($this->result['len']) ; $i++) {
					if ($small_block_number < 6) {
						// 记录大图
						if (empty($big_img) and $this->result['big_img'][$i]) {
							$big_img = $this->result['big_img'][$i][0];
						}
						if ($this->analyse_is_big_block($i)) {
							$clip .= $tmp;
							$clip .= $this->result['html'][$i];
							$tmp = '';
							$small_block_number = 0;
							// 记录摘要
							if (strlen($summary) < 100 and $this->result['txt_len'][$i]>strlen($summary)) {
								$summary = $this->result['txt'][$i];
							}
						} else {
							$tmp .= $this->result['html'][$i];
							$small_block_number ++;
						}
					} else {
						if (strlen(preg_replace('/\s/', '', $this->function_strip_tags($clip))) > 300) {	// 如果已经获得了一部分代码，那么就可以结束了
							$tmp = '';
							$small_block_number = 0;
							break;
						} else {
							$small_block_number = 0;					// 如果获得的太少，那么还得继续查找
						}
					}
				}
				$return = array (
					'clip'		=> $this->replace_in_return_for_spicial_site($this->before_body . $this->style . $clip . $this->after_body),
					'summary'	=> mb_strimwidth(trim($summary), 0, 250, '..', 'utf8'),
					'big_imgs'	=> array($big_img),
				);
			}
			// 如果没有找到center block，或者找到之后没有解析出summary（例如taobao）那么尝试找出三张大图
			if (empty($return['summary'])) {
				$big_imgs = $this->find_3_big_img();
				if ($big_imgs) {
					$return = array(
						'big_imgs'	=> $big_imgs,
					);
				}
			}
			/*
			 * 检查html中是否包括“<html”的字样，
			 *	如果包括，说明保存的是整个页面，那么进行clip，
			 *	否则，说明是选中之后进行保存的，不要进行clip，但是要去抓摘要和大图
			 */ 
			
			if (preg_match('/<html/i', $this->before_body)) {
				return $return;
			} else {
				$return['clip'] = '';
				return $return;
			}
		} return false;
	}
	
	private function replace_in_return_for_spicial_site ($return_html) {
		if (preg_match('/sina.com/i', $this->base_url)) {
			return preg_replace('/class\s*=\s*["|\']SG_conn["|\']/i', '', $return_html);
		} elseif (preg_match('/taobao.com/i', $this->base_url)) {
			return preg_replace('/style=".*?"/i', '', $return_html);
		} else {
			return $return_html;
		}
	}
	private function analyse_center_blocks () {
		$keys = $this->center_block_rule_1();		// (min, max)
		if ($keys) {
//			pre_echo('center 1');
//			pre_echo($keys);
			return $keys;
		} else {
			// 用第二种方式（大图）来查找
			$keys = $this->center_block_rule_2();	// (min, max)
			if ($keys) {
//				pre_echo('center 2');
//				pre_echo($keys);
				return $keys;
			}
		}
//		pre_echo('no center');
		return false;
	}
	private function center_block_rule_1 () {
		$array = $this->result['len'];
		arsort($array);			// 从大到小，按len排序，保持key不变
		$keys = false;
		foreach ($array as $k=>$v) {
			if ($keys === false) {			// 先找出最大的block
				// 规则：最大的block至少要占10%以上，否则就没法算了
				// 新浪新闻的正文才占4%，太过分啦！！！！！！！！！
				if ($this->result['per_len'][$k] > 10) {
					// 规则：大于90% 或者 (a链接小于20%，txt比例大于50%)
					if ( ($this->result['per_a'][$k]<20 and $this->result['txt_in_html'][$k]>70) or $this->result['per_len'][$k] > 85 ) {
						// 记录k
						$keys[] = $k;					// 最大的block的key
						$big_per_min = floor($this->result['per_len'][$k] * 0.7);	//最大的block的百分比向下浮动30%来查找近似的block
					} 
				} else {
					break;
				}
			} else {					// 然后向下浮动30%来查找近似的block，构成核心部分的blocks
				if ($this->result['txt_len'][$k]>1000 or $this->result['per_len'][$k] >= $big_per_min) {
					if ($this->result['per_a'][$k]<20 and $this->result['txt_in_html'][$k]>50) {
						// 记录k
						$keys[] = $k;
					}
				} else {
					break;
				}
			}
		}
		
		if (is_array($keys)) {
			sort($keys);
			$min = $keys[0];
			$max = array_pop($keys);
			return array (
				'min'	=> $min,
				'max' => $max
			);
		} else return false;
	}
	private function center_block_rule_2 () {
		// 以大图为中心来向上、向下找center blocks
		if (is_array($this->result['img_len'])) {
			$array = $this->result['img_len'];
			arsort($array);			// 从大到小，按len排序，保持key不变
			
			$key_max = $key_min = false;
			foreach ($array as $k=>$v) {	// 获取第一个
				if ($v > 0) {
					$key_max = $key_min = $k;	// 记录k
				}
				break;			// 只需要获取最大的
			}
			
			$none_big_img_block_number = 0;
			if ($key_max and $key_min) {
				// 向前查找
				for ($i = $key_min -1 ; $i >= 0 ; $i--) {
					if ($none_big_img_block_number < 5) {
						if ($this->result['big_img'][$i]) {		// 如果有大图
							$key_min = $i;				// 记录k最小值
							$none_big_img_block_number = 0;
						} else {
							$none_big_img_block_number++;		// 记录空白次数
						}
					} else {
						break;			// 空白次数太多就中断
					}
				}
				// 向后查找
				$none_big_img_block_number = 0;
				for ($i = $key_max +1 ; $i < sizeof($this->result['img_len']) ; $i++) {
					if ($none_big_img_block_number < 5) {
						if ($this->result['big_img'][$i]) {		// 如果有大图
							$key_max = $i;				// 记录k最大值
							$none_big_img_block_number = 0;
						} else {
							$none_big_img_block_number++;		// 记录空白次数
						}
					} else {
						break;			// 空白次数太多就中断
					}
				}
				return array(
					'min'	=> $key_min,
					'max'	=> $key_max
				);
			} else return false;
		} else return false;
	}
	private function find_3_big_img () {
		if (is_array($this->result['img_len'])) {
			$array = $this->result['img_len'];
			arsort($array);			// 从大到小，按len排序，保持key不变
			$big_imgs = array ();
			foreach ($array as $k=>$v) {
				if ($v > 0) {
					if (count($big_imgs) < 3) {
						if ($this->result['big_img'][$k]) {
							$big_imgs = array_merge($big_imgs, $this->result['big_img'][$k]) ;		// 合并数组
						}
					} else {
						break;
					}
				} else {
					break;
				}
			}
			if (count($big_imgs)>1) {		// 至少返回2张图片 ，最多3张
				return array_slice($big_imgs, 0, 3);
			} else return false;
		} else return false;
	}

	private function analyse_is_big_block ($k) {
		if (	
			$this->rule_1_big_img($k)		or 
			$this->rule_2_big_txt($k)		or
			$this->rule_3_medium_txt($k)		or
			$this->rule_4_tiny_txt($k)
		) return true;
		else return false;
	}
	private function rule_1_big_img ($k) {
		if ($this->result['big_img'][$k]) {
			return true;
		}
		return false;
	}
	private function rule_2_big_txt ($k) {
		if (	$this->result['txt_len'][$k]		> 1000	and 
			$this->result['per_len'][$k]		> 30		and 
			$this->result['per_a'][$k]		< 20		and 
			$this->result['txt_in_html'][$k]	> 50
		) {
			return true;
		}
		return false;
	}
	private function rule_3_medium_txt ($k) {
		if (	$this->result['txt_len'][$k]		> 300		and 
			$this->result['per_len'][$k]		> 5		and 
			$this->result['per_a'][$k]		< 20		and 
			$this->result['txt_in_html'][$k]	> 50
		) {
			return true;
		}
		return false;
	}
	private function rule_4_tiny_txt ($k) {
		if (	$this->result['txt_len'][$k]		> 100		and 
			$this->result['per_a'][$k]		< 15		and
			$this->result['txt_in_html'][$k]	> 85
		) {
			return true;
		}
		return false;
	}
	private function per_a ($html) {
		if ($html) {
			// 匹配出a标签
			preg_match_all('/<a\s+.+?>(.+?)<\/a>/i', $html, $match);
			// 计算a链接中间部分内容的长度
			$len_a = 0;
			if (is_array($match[1])) {
				foreach ($match[1] as $v) {
					$v = preg_replace('/<img\s+.+?>/i', '123456789012345678901234567890', $v);		// 把a链接中的img作为30个字节来看待
					$len_a += strlen($v);
				}
			}
			if ($len_a > 0) {
				// 去掉a标签以后，剩余的文本长度
				$txt_len_without_a = strlen($this->function_strip_tags(preg_replace('/<a\s+.+?>.+?<\/a>/i', '', $html)));
				if ($txt_len_without_a > 0) {
					return intval(floor( $len_a / ($len_a + $txt_len_without_a) * 100 ));		// 返回a的比例
				} else return 100;			// 去掉a标签后，剩余的文本长度为0，那么这个block中全都是a标签啊，返回百分比为 100
			} else return 0;				// a标签占的百分比为0
		} else return 0;
	}
	private function per_txt_in_html ($html) {
		$html = preg_replace('/^<div.*?>|^<table.*?>/i', '', $html, 1);		// 如果首尾有标签，那么去掉
		$html = preg_replace('/<\/div>$|<\/table>$/i', '', $html, 1);			// 如果首尾有标签，那么去掉
		// 去掉一些格式标签
		$html = preg_replace('/<img.*?>|<strong.*?>|<\/strong>|<b.*?>|<\/b>|<br.*?>|<wbr.*?>|<i.*?>|<\/i>|<font.*?>|<\/font>|<span.*?>|<\/span>|<p.*?>|<\/p>|<li.*?>|<\/li>|<embed.*?>/i', '', $html);
		$html = preg_replace( '/style\s*=\s*".*?"/i', '', $html);				// 去掉标签中的style 
		$txt = $this->function_strip_tags($html);
		$txt = preg_replace('/\s/', '', $txt);
		
		if ($html) {
			return intval(floor(strlen($txt) / strlen($html) * 100));
		} else return 0;
	}
	/**
	 * strip_tags函数在遇到如下情况时会出错<div fc05="" fc11="" nbw-blog="" ztag=""  js-fs2"="">
	 * 所以先用正则判断一下
	 * @param type $html
	 */
	private function function_strip_tags ($html) {
		return strip_tags(preg_replace('/<(div|a|span|td|table).*?>/is', '', $html));
	}
	/**
	 * return array (
	 *	big_img_urls = array ()
	 *	img_as_byte
	 * )
	 */
	private function parse_img_in_block ($html) {
		$return = false;
		preg_match_all('/<img\s+.+?>/i', $html, $images);
		$images = $images[0];
		if (is_array($images)) {
			foreach ($images as $img) {
				$mark = $this->get_mark($img);
				if ($mark['type'] == 'big') {
					$return['img_as_byte'] += $this->img_as_len($mark);
					// 匹配出src
					preg_match('/src\s*=\s*[\'|"](.+?)[\'|"]/i', $img, $match);
					$return['big_img_urls'][] = $match[1];
				}
			}
		}
		return $return;
	}
	private function is_big_img_need_download ($size) {
		if ($size['width'] >= $this->img_width_max and $size['height'] >= $this->img_height_max) {
			return true;
		} else return false;
	}
	private function img_as_len ($size) {
		if ($this->is_big_img_need_download($size)) {
			// 只有大图片才映射成文字字节数
			// 这里把300X200的图片作为1000个字节来看（也是估计的）
			// 按这个比例来计算图片对应的字节大小
			return intval($size['width'] * $size['height'] * 1000 / 300 / 200);
		} else return 0;
	}
	/**
	 * 检查src是否是绝对url，如果是相对的，则加上页面的url地址
	 *
	 * @param String $page_url
	 * @param String $src
	 * @return String
	 */
	public static function relative_to_absolute ($url, $base_url) {
		$url		= trim($url);
		$base_url 	= trim($base_url);
		
		$return = false;
		if (preg_match('/^http:\/\/|^https:\/\//i', $url)) {
			$return = $url;
		} elseif ($url and preg_match('/^http:\/\/|^https:\/\//i', $base_url)) {
			$parse_base = parse_url($base_url);
			if ($parse_base['scheme'] and $parse_base['host']) {
				if (substr($url, 0, 1) == '/') {		// 以“/”开头，绝对根路径
					$return = $parse_base['scheme'] . '://' . $parse_base['host'] . '/' . $url;
				} else {					// 相对路径
					if (empty($parse_base['path'])) {							// base url只有域名，那么直接加在域名后面就行了
						$return = $parse_base['scheme'] . '://' . $parse_base['host'] . '/' . $url;
					} elseif (substr($parse_base['path'], -1, 1) == "/") {				// 以“/”结尾，那么直接加在path后面就行了
						$return = $parse_base['scheme'] . '://' . $parse_base['host'] . $parse_base['path'] . $url;
					} else {
						// 判断path部分是路径还是文件名，有什么好办法呢？
						$explode = explode('/', $parse_base['path']);
						$pop = array_pop($explode);
						if (preg_match('/\./', $pop)) {							// 最后一段中包含“.”，就认为他是文件名
							$return = $parse_base['scheme'] . '://' . $parse_base['host'] . '/' . implode('/', $explode) . '/' . $url;
						} else {										// 路径
							$return = $parse_base['scheme'] . '://' . $parse_base['host'] . $parse_base['path'] . '/' . $url;
						}
					}
				}
			}
		}
		
		if ($return) {
			$parse = parse_url($return);
			return $parse['scheme'] . '://' . $parse['host'] . preg_replace('/\/+/', '/', $parse['path']);
		} else return false;
	}
	
	private function mark_as ($img, $flag, $width=0, $height=0) {
		if ($flag == 'pass') {
			$img = preg_replace('/ureadmemark=".+?"/', '', $img);
			return preg_replace('/\/>$|>$/', ' ureadmemark="pass"/>', $img);
		} elseif ($flag == 'passbyfailed') {
			$img = preg_replace('/ureadmemark=".+?"/', '', $img);
			return preg_replace('/\/>$|>$/', ' ureadmemark="passbyfailed"/>', $img);
		} elseif ($flag == 'uncheck') {
			$img = preg_replace('/ureadmemark=".+?"/', '', $img);
			return preg_replace('/\/>$|>$/', ' ureadmemark="uncheck"/>', $img);
		} elseif ($flag == 'big' and $width>0 and $height>0) {
			$width = intval($width);
			$height = intval($height);
			$img = preg_replace('/ureadmemark=".+?"/', '', $img);
			return preg_replace('/\/>$|>$/', ' ureadmemark="'.$width.'big'.$height.'"/>', $img);
		} elseif ($flag == 'failed') {
			preg_match('/ureadmemark="failed(\d*)"/', $img, $match);
			$failed_times = intval($match[1]);
			$img = preg_replace('/ureadmemark=".+?"/', '', $img);
			return preg_replace('/\/>$|>$/', ' ureadmemark="failed'.($failed_times+1).'"/>', $img);
		} else {
			return $img;
		}
	}
	private function get_mark ($img) {
		$return = false;
		preg_match('/ureadmemark="(.+?)"/', $img, $match);
		if ($match) {
			if ($match[1] == 'pass') {
				$return = array (
					'type' => 'pass'
				);
			} elseif ($match[1] == 'passbyfailed') {
				$return = array (
					'type' => 'passbyfailed'
				);
			} elseif ($match[1] == 'uncheck') {
				$return = array (
					'type' => 'uncheck'
				);
			} else {
				preg_match('/^(\d+)big(\d+)$/', $match[1], $big);
				if ($big) {
					$return = array (
						'type'		=> 'big',
						'width'	=> $big[1],
						'height'	=> $big[2],
					);

				} else {
					preg_match('/^failed(\d*)$/', $match[1], $failed);
					if ($failed) {
						$return = array (
							'type'			=> 'failed',
							'failed_times'	=> intval($failed[1]),
						);
					}
				}
			}
		}
		return $return;
	}
	/*
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 *	第二步：
	 *		反复检查html中的img（因为一次检查的话，有可能因为curl超时而无法检查img的尺寸）
	 *		下载成功的大图标记为big，并替换进去
	 *		如果没有需要检查的了，就返回false
	 * 
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 */
	public function download_big_img_and_replace ($html) {
		// 匹配所有img，排除pass和big
		preg_match_all('/<img\s+.+?>/i', $html, $images);
		$images = $images[0];
		if (is_array($images) and count($images)>0) {
			foreach ($images as $k=>$img) {
				$mark = $this->get_mark($img);
				if ($mark['type'] == 'pass' or $mark['type'] == 'passbyfailed' or $mark['type'] == 'big') {
					unset($images[$k]);
				}
			}
			// 剩余的是uncheck，failed。（还有未标记的？？应该都标记了，如果有未标记的，就是前面的程序出错了）
			if (count($images) > 0) {
				/**
				 * 防止超时，想不到什么好办法了，只能限制get_size_by_remote的次数
				 * curl里设定了等待和执行的timeout，那么用max_execution_time/(curl_timeout + curl_connecttimeout)就差不多了吧
				 */
				
				$max_execution_time = intval(ini_get('max_execution_time'));
				$curl_connect_timeout = 1;
				$curl_timeout = 1;
				
				$max_remote_times = floor($max_execution_time / ($curl_timeout + $curl_connect_timeout));
				$m = 0;
				
				foreach ($images as $img) {
					if ($m > $max_remote_times) {
						break;				// 次数太多了，为了防止超时，中断吧
					}
					$m++;
					preg_match('/src\s*=\s*[\'|"](.+?)[\'|"]/i', $img, $match);
					$size = $this->get_size_by_remote($match[1], $curl_timeout, $curl_connect_timeout);		// (width, height, url, is_big)
					if ($size) {
						if ($size['is_big'] and $size['width'] and $size['height'] and $size['url']) {
							// 替换新url进img
							$new_img = preg_replace('/src\s*=\s*[\'|"](.+?)[\'|"]/i', 'src="'.$size['url'].'"', $img);
							// 替换img进html，标记为big
							$html = str_replace($img, $this->mark_as($new_img, 'big', $size['width'], $size['height']), $html);
//							pre_echo('is_big : '.htmlspecialchars($new_img));
						} else {
							// 替换img进html，标记为pass
							$html = str_replace($img, $this->mark_as($img, 'pass'), $html);
//							pre_echo('pass : '.htmlspecialchars($img));
						}
					} else {
						// 标记为failed，检查failed次数，如果太多的话，就标记为pass
						$mark = $this->get_mark($img);
						if ($mark['type'] == 'failed') {
							if ($mark['failed_times']>2) {		// failed次数太多了
								// 替换img进html，标记为passbyfailed
								$html = str_replace($img, $this->mark_as($img, 'passbyfailed'), $html);
//								pre_echo('passbyfailed : '.htmlspecialchars($img));
							} else {
								// 替换img进html，标记为failed，次数或自动+1
								$html = str_replace($img, $this->mark_as($img, 'failed'), $html);
//								pre_echo('failed : '.htmlspecialchars($img));
							}
						} else {
							// 替换img进html，标记为failed，次数或自动+1
							$html = str_replace($img, $this->mark_as($img, 'failed'), $html);
//							pre_echo('failed : '.htmlspecialchars($img));
						}
					}
				}
				return $html;
			} else return false;
		} else return false;
	}
	private function get_size_by_remote ($url, $timeout=1, $connect_timeout=1) {
		$url = trim($url);
		if ($url and preg_match('/^http:\/\/|^https:\/\//i', $url)) {
			
			$ch = curl_init();
			curl_setopt($ch,	CURLOPT_URL,	$url);
			if (preg_match('/^https:\/\//i', $url)) {
				curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);			// 如果是https开头的，那么关闭ssl证书验证
			}
			curl_setopt ($ch,	CURLOPT_RETURNTRANSFER, 1);				// 关闭输出，返回字符串
			curl_setopt($ch,	CURLOPT_TIMEOUT, $timeout);				// The maximum number of seconds to allow cURL functions to execute. 
			curl_setopt($ch,	CURLOPT_CONNECTTIMEOUT, $connect_timeout);	// The number of seconds to wait while trying to connect. Use 0 to wait indefinitely. 
			curl_setopt($ch,	CURLOPT_FOLLOWLOCATION,1); 
			curl_setopt($ch,	CURLOPT_HEADER,1);						// 返回header
			$content = curl_exec($ch);
			curl_close($ch);
			
			if ($content) {
				$explode = explode("\r\n\r\n", $content);						// 把header 部分分离出来，注意：header可能有多个
				$last_header = $explode[(count($explode)-2)];					// 倒数第二个，也就是最后一个header
				$img_content = $explode[(count($explode)-1)];				// 最后一部分是真正的图片内容
				preg_match('/Content-Length\s*:\s*(\d+)/i', $last_header, $match);	// 匹配出header中的Content-Length，用来判断下载的文件是否完整
				if (!isset($match[1]) or ($match[1]>0 and $match[1] == strlen($img_content))) {		// 如果在header中没有Content_Length就不检查了
					$tmpfile = tempnam(sys_get_temp_dir(), '');				// 系统下的临时文件
					$fp = fopen($tmpfile, 'w');
					fwrite($fp, $img_content);							// 写入临时文件
					fclose($fp);
					
					$size = getimagesize($tmpfile);						// 获取临时文件的图像信息
					if ($size) {
						if ($this->is_big_img_need_download(array('width'=>$size[0], 'height'=>$size[1])) and ($size[2]==1 or $size[2] == 2 or $size[2] == 3)) {
							$name = basename($tmpfile) . (($size[2] == 1) ? '.gif' : (($size[2] == 2) ? '.jpg' : '.png'));
							$www_url = $this->img_www_path . $name;
							
							if (preg_match('/127.0.0.1/', $_SERVER['SERVER_ADDR']) or preg_match('/127.0.0.1/', $_SERVER['LOCAL_ADDR'])) {
								$local_file = PATH_HTDOCS . 'img/' . $name;
								// 本地文件存储 ----------------------------------------------------------------------
								rename($tmpfile, $local_file);
								chmod($local_file, 0644);  
								return array (
									'width'	=> $size[0],
									'height'	=> $size[1],
									'url'		=> $www_url,
									'is_big'	=> true,
								);
							} else {
								$local_file = $this->img_local_path . $name;
								// 使用又拍云来存储 ----------------------------------------------------------------------
								$upyun = new UpYun(UPYUN_BUCKETNAME, UPYUN_USERNAME, UPYUN_PASSWORD);							
								$opts = array(
									UpYun::CONTENT_MD5 => md5(file_get_contents($tmpfile))
								);
								$fh = fopen($tmpfile, 'rb');
								$rsp = $upyun->writeFile($local_file, $fh, false, $opts);   // 上传图片，!! 不 !!自动创建目录
								fclose($fh);

								if (isset($rsp['x-upyun-width']) and isset($rsp['x-upyun-height'])) {		// 上传成功
									return array (
										'width'	=> $size[0],
										'height'	=> $size[1],
										'url'		=> $www_url,
										'is_big'	=> true,
									);
								}

							}
						} else {
							return array (
								'width'	=> $size[0],
								'height'	=> $size[1],
								'url'		=> $url,
								'is_big'	=> false,
							);
						}
					}
					unlink($tmpfile);
				}
			}
		}
		return false;
	}	
	
	/*
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 *	第一步：
	 *		在保存文章时，
	 *		替换了文章中的link和img标签为标准绝对路径，对img做了标记并且去除了换行和空白
	 *		重复的图片标记为pass
	 * 
	 * ###########################################################################################
	 * ###########################################################################################
	 * 
	 */
	public function replace_for_save ($url, $html) {
		$html = preg_replace('/<script.*?<\/script>|<noscript.*?<\/noscript>|<!--.*?-->|<iframe.*?<\/iframe>|<form.*?>|<\/form>|<input.*?>/is', '', $html);
		$html = $this->replace_absolute_link_for_save($url, $html);
		$html = $this->replace_absolute_img_for_save($url, $html);
		return $html;
	}
	/**
	 * 将html中的全部img标签中的src替换成绝对路径，
	 *	如果已经有ureadmemark，那就不用处理了
	 *	如果有特殊的src也替换到标准src中来
	 *	如果在tag中就检查到了该图片是小图，那么标记ureadmepassed，以后就忽略该图片了
	 *	用于第一次保存文章的时候
	 * 
	 * @param string $url
	 * @param string $content
	 * @return string
	 */
	private function replace_absolute_img_for_save ($url, $content) {
		$this->img_ignor = array ();
		$this->img_ok['src_md5'] = array ();
		$this->img_ok['img'] = array ();
		// 匹配出img标签
		preg_match_all('/<img\s+.+?>/is', $content, $imgs);
		$imgs = $imgs[0];
		if (is_array($imgs)) {
			$i = 0;
			foreach ($imgs as $original_img) {		// 单条<img .... />
				// 先检查有没有ureadmemark
				if (preg_match('/ureadmemark/i', $original_img)) {
					// 如果已经做了标记，那么就不用处理了
				} else {
					// 匹配出<img 标签中的src
					preg_match('/src\s*=\s*[\'|"](.+?)[\'|"]/is', $original_img, $src);
					if ($src[0] and $src[1]) {
						$original_src	= $src[1];									// 原来的标准src
						$new_src		= $this->get_real_src($original_img, $url);				// 一些网站特殊的src，实际上是真正的src，获得后转换成绝对地址
						if ($new_src and $original_src <> $new_src) {
							$new_img = str_replace($original_src, $new_src, $original_img);		// 把绝对地址替换回原来的img标签中去
						} else {
							$new_src	= trim($original_src);
							$new_img	= $original_img;
						}
						$new_img = preg_replace('/\s{2,}|\t|\r|\n/', '', $new_img);						// 在新的img中替换掉连续空格，制表符，回车，换行

						// 检查src是否重复出现
						$src_md5 = md5($new_src);
						if (in_array($src_md5, $this->img_ignor)) {
							// 重复出现，标记为pass，替换回html中
							$content = str_replace($original_img, $this->mark_as($new_img, 'pass'), $content);	
//							pre_echo('in Save, 在忽略列表中，标记为pass : '.htmlspecialchars($new_img));
						} else {
							$key = array_search($src_md5, $this->img_ok['src_md5']);
							if ($key === false) {							// 没有找到，添加到ok中
								// 检查size
								$size = $this->get_size_by_tag($new_img);								// 根据tag中的内容来返回图片的大小
								if ($size and !$this->is_big_img_need_download($size)) {
									// 检查到了图片的尺寸，而且不是大图，那么标记忽略，那么下次的时候就不再检查了
									$new_img = $this->mark_as($new_img, 'pass');
									$content = str_replace($original_img, $new_img, $content);	
//									pre_echo('in Save, 不是大图，标记为pass : '.  htmlspecialchars($new_img));
								} else {
									// 标记为未检查
									$new_img = $this->mark_as($new_img, 'uncheck');
									$content = str_replace($original_img, $new_img, $content);
//									pre_echo('in Save, uncheck : '.htmlspecialchars($new_img));
								}
								// 同时记录src md5和img，用于再次检查时遇到重复的src时，把原来的也pass掉
								$this->img_ok['src_md5'][$i]	= $src_md5;
								$this->img_ok['img'][$i]	= $new_img; // 注意new img中已经完成mark
							} else {									// 找到重复的
								// 把新img标签标记为pass，替换回html中
								$content = str_replace($original_img, $this->mark_as($new_img, 'pass'), $content);
//								pre_echo('in Save, 找到一张重复图片，标记为pass : '.htmlspecialchars($new_img));
								// 找到上一张重复的图片，标记为pass，并替换回去
								$content = str_replace($this->img_ok['img'][$key], $this->mark_as($this->img_ok['img'][$key], 'pass'), $content);
//								pre_echo('in Save, 重复图片的原来那个，标记为pass : '.htmlspecialchars($this->img_ok['img'][$key]));
								// 添加到忽略列表中
								$this->img_ignor[] = $src_md5;
								// 从ok列表中移除
								unset($this->img_ok['src_md5'][$key]);			
								unset($this->img_ok['img'][$key]);
							}
						}
					} else {
						// 没有找到src？？还真有没有src的，<img id="comment_check_img" align="absmiddle" style="display: none;"> 在这里 http://blog.sina.com.cn/s/blog_4ac1e3c601019tba.html?tj=1
						$new_img = preg_replace('/\s{2,}|\t|\r|\n/', '', $original_img);		// 替换掉连续空格，制表符，回车，换行
						// 标记为pass，并替换回去
						$content = str_replace($original_img, $this->mark_as($new_img, 'pass'), $content);
//						pre_echo('<div style="padding:10px; background-color:red; color:white; font-size:16px;">in Save, 没有找到src？？ : '.htmlspecialchars($new_img).'</div>');
					}
				}
				$i++;
			}// </foreach>
		}
		return $content;
	}
	/**
	 * 将html中的全部<link标签中的src替换成绝对路径，
	 *	用于第一次保存文章的时候
	 * 
	 * @param string $url
	 * @param string $content
	 * @return string
	 */
	private function replace_absolute_link_for_save ($url, $content) {
		// 匹配link标签
		preg_match_all('/<link\s+.+?>/is', $content, $links);
		$links = $links[0];
		if (is_array($links)) {
			foreach ($links as $original_link) {		// 单条<link...
				// 匹配出<link 标签中的href
				preg_match('/href\s*=\s*[\'|"](.+?)[\'|"]/is', $original_link, $href);
				if ($href[0] and $href[1]) {
					$original_href = $href[1];
					// 把相对地址变成绝对地址
					$absolute_href = $this->relative_to_absolute($url, $original_href);
					if ($original_href <> $absolute_href) {
						// 把绝对地址替换回原来的link标签中去
						$absolute_link = str_replace($original_href, $absolute_href, $original_link);
					} else {
						// 一样的不用替换
						$absolute_link = $original_link;
					}
					// 去掉换行、连续空格、制表符
					$absolute_link = preg_replace('/\s{2,}|\t|\r|\n/', '', $absolute_link);
					// 把新link标签替换回html中
					$content = str_replace($original_link, $absolute_link, $content);
				}
			}
		}
		return $content;
	}
	private function get_real_src ($img, $base_url) {
		if (preg_match('/img\.ureadme\.cn/i', $img)) {
			return false;
		} else {
			$pattern_array = array (
				'data-lazyload',				// 大众点评论坛里的
				'real_src',					// 新浪blog的
				'file',						// 破洛洛论坛
				'zoomfile',					// 破洛洛论坛
				'src',
			);
			foreach ($pattern_array as $pattern) {
				preg_match('/'.$pattern.'\s*=\s*["|\'](.+?)["|\']/i', $img, $match);
				if ($match[1]) {
					return trim($this->relative_to_absolute($match[1], $base_url));
				}
			}
			return false;
		}
	}
	private function get_size_by_tag ($img) {
		if ($img) {
			$img = preg_replace('/\s+/', '', $img);		// 去掉所有空格，为了下面好匹配

			preg_match('/width:(\d+)/i', $img, $match);		// 优先检查style中的宽度
			if ($match[1]) {
				$width = $match[1];
			} else {
				preg_match('/width=["|\'](\d+)/i', $img, $match);		// 检查img标签中的宽度
				if ($match[1]) {
					$width = $match[1];
				} else {
					$width = false;
				}
			}
			if ($width) {
				preg_match('/height:(\d+)/i', $img, $match);		// 优先检查style中的高度
				if ($match[1]) {
					$height = $match[1];
				} else {
					preg_match('/height=["|\'](\d+)/i', $img, $match);		// 检查img标签中的高度
					if ($match[1]) {
						$height = $match[1];
					} else {
						$height = false;
					}
				}
				if ($height) {
					return array (
						'width'	=> $width,
						'height'	=> $height,
					);
				}
			}
		}
		return false;
	}
}
?>